#ifndef __MVS_MS_APIS_H__
#define __MVS_MS_APIS_H__

#include "mvdef.h"
#include "mvdef_media.h"

MVCXX_EXTERN_BEGIN

/**
 * @brief MS 模块两大基本调用流程参考 \n
 *        1、数据提供者： mvs_ms_new -> mvs_ms_video/audio_data_stash -> 重复不停stash ... -> (某些特定场景可能需要)mvs_ms_clear
 *        2、单通道数据使用者：
 *              (句柄重复利用的话) mvs_ms_data_drop -> 
 *              mvs_ms_data_prepare -> (可能需要类型判断) -> (非I帧时可以wait I frame)mvs_ms_data_pop ->
 *              mvs_ms_data_pop -> 重复不停pop ... -> 
 *              (完全不需要的时候) mvs_ms_data_drop
 *        3、双通道数据使用者：
 *              (句柄重复利用的话) mvs_ms_data_drop -> 
 *              mvs_ms_data_chn_sync_prepare -> (可能需要类型判断) -> (非I帧时可以重复sync pop对类型判断拿到I帧为止)mvs_ms_data_chn_sync_pop ->
 *              mvs_ms_data_chn_sync_pop -> 重复不停sync pop ... ->
 *              (完全不需要的时候) mvs_ms_data_drop
 */

/** 请求视频I帧回调函数 */
typedef mv_void_t(*p_request_i_frame_f)(mvdef_media_channel_e e_channel);

typedef struct _ms_data_info
{
    mv_time_t  ts_abs_msec;     /* system real timestamp of this data (ms) ; data using (if less than 1000000000*1000LL, some applications use it as mono timestamp)*/
    mv_time_t  ts_ref_msec;     /* system mono timestamp of this data (ms) ; queue using */
    mv_u32_t   u32_data_size;   /* data size */
    mv_vptr_t  p_data_vaddr;    /* data virtual memory addr */
    mv_u8_t    u8_type;         /* type of media data [mvdef_media_frame_type_e] */
    mv_u8_t    u8_usrdata;      /* user's data */
    mv_u8_t    u8_reserve[2];   ///< (保留)
}mvs_ms_data_info_t;

typedef struct 
{
    mvdef_media_channel_e e_chn;
    mv_time_t             ts_pre_msec;     ///< 预录时长（毫秒）
    mvs_ms_data_info_t    stru_data_info;  ///< 取出数据节点信息
}mvs_ms_data_handle_t;

/**
 * 音视频流信息
 */
typedef struct _ms_channel_info{
    mvdef_media_channel_e e_chn;                    ///< 音视频通道
    union {
        struct {
            mv_u16_t u16_x_px;                      ///< 水平像素大小
            mv_u16_t u16_y_px;                      ///< 垂直像素大小
            p_request_i_frame_f f_request_i_frame;  ///< 请求获取I帧
            mv_u32_t u32_gop;                       ///< I帧间隔(秒)
        }stru_video;
        struct {
            mv_u32_t u32_sample_rate;               ///< 音频采样率
            mv_u32_t u32_sample_bits;               ///< 音频采样位数
            mv_u8_t  u8_channel_num;                ///< 音频采样通道数
        }stru_audio;
    };
    mvdef_media_codec_e  e_codec;                   ///< 编码类型
    mv_u32_t             u32_bitrate;               ///< 比特率(bps)
    mv_u32_t             u32_max_fps;               ///< 帧率
    mv_s32_t             s32_max_perframe;          ///< 单帧最大限制(Byte)，0：代表默认 bitrate*2
    mv_u32_t             max_cache_seconds;         ///< 最大缓存秒数 0：代表默认8秒
}mvs_ms_channel_info_t;

/**
 * @brief 创建新的流通道
 * 
 * @param pstru_chn_info [in]  流通道信息
 * @return 创建结果
 * - success : MV_EC_SUCCESS
 * -  failed : MV_EC_INVALID_ARGS (空参数或非法通道)
 *             MV_EC_REDEF        (重复创建)
 *             MV_EC_MALLOC_FAILED(内存申请失败)
 */
mv_s32_t mvs_ms_new(mvs_ms_channel_info_t *pstru_chn_info);
/**
 * @brief 更新通道参数（仅部分可动态更改生效）
 * 
 * @param pstru_chn_info [in]  流通道信息
 * @return 更新结果
 * - success : MV_EC_SUCCESS
 * -  failed : MV_EC_INVALID_ARGS (空参数或非法通道)
 *             MV_EC_REDEF        (未创建)
 * 
 * @note 当前仅video的gop可更改生效
 */
mv_s32_t mvs_ms_update(mvs_ms_channel_info_t *pstru_chn_info);
/**
 * @brief 销毁流通道（暂未实现）
 * 
 * @param e_chn [in] 目标通道
 * @param s32_await_timeout_ms [in] 销毁等待超时时间(0为异步不等待)
 * @return 销毁结果
 */
mv_s32_t mvs_ms_destroy(mvdef_media_channel_e e_chn, mv_s32_t s32_await_timeout_ms);
/**
 * @brief 获取目标通道的信息
 * 
 * @param e_chn [in] 目标通道
 * @param pstru_chn_info [in] 流通道信息缓存地址(可选，NULL时相当于查询通道状态)
 * @return 操作结果
 * - success : MV_EC_SUCCESS
 * -  failed : MV_EC_NOT_INIT      (通道未初始化)
 *             MV_EC_INVALID_ARGS  (非法通道)
 */
mv_s32_t mvs_ms_channel_info(mvdef_media_channel_e e_chn, mvs_ms_channel_info_t *pstru_chn_info);
/**
 * @brief 通道数据清空
 * 
 * @param e_chn [in] 目标通道
 * @param s32_await_timeout_ms [in] 清空等待超时时间(0为异步不等待)
 * @return 清空结果
 * - success : MV_EC_SUCCESS
 * @note 出于通道操作安全考虑，clear后仍然会存在两个最新的数据节点
 */
mv_s32_t mvs_ms_clear(mvdef_media_channel_e e_chn, mv_s32_t s32_await_timeout_ms);
/**
 * @brief 视频数据存入
 * 
 * @param e_video_chn [in] 目标视频通道
 * @param pstru_info  [in] 视频数据信息结构
 * @return 存入操作结果
 * - success : MV_EC_SUCCESS
 * -  failed : MV_EC_NOT_INIT      (通道未初始化)
 *             MV_EC_RESOURCE_LEAK (通道已满，暂时无法存入)
 *             MV_EC_MALLOC_FAILED (内存申请失败)
 * 
 * @note 数据的ts_mono_msec必须保证单调递增
 * @attention 注意，如果数据的ts_abs_msec小于1000000000*1000LL，部分应用会认为是单调时间戳，有特殊处理
 */
mv_s32_t mvs_ms_video_data_stash(mvdef_media_channel_e e_video_chn, mvs_ms_data_info_t *pstru_info);
/**
 * @brief 音频数据存入
 * 
 * @param e_video_chn [in] 目标视频通道
 * @param pstru_info  [in] 视频数据信息结构
 * @return 存入操作结果
 * - success : MV_EC_SUCCESS
 * -  failed : MV_EC_NOT_INIT      (通道未初始化)
 *             MV_EC_RESOURCE_LEAK (通道已满，暂时无法存入)
 *             MV_EC_MALLOC_FAILED (内存申请失败)
 * 
 * @note 数据的ts_mono_msec必须保证单调递增
 * @attention 注意，如果数据的ts_abs_msec小于1000000000*1000LL，部分应用会认为是单调时间戳，有特殊处理
 */
mv_s32_t mvs_ms_audio_data_stash(mvdef_media_channel_e e_audio_chn, mvs_ms_data_info_t *pstru_info);

typedef enum _ms_iframe_offset_whence
{
    MS_ISEEK_INVALID      = 0,
    MS_ISEEK_HDR_FORWARE  = 1,    ///< 从最早I帧开始偏移正向ioff，<=0均返回最早一个I帧
    MS_ISEEK_END_BACKWARE = 2,    ///< 从最后I帧开始偏移反向ioff，<=0均返回最后一个I帧
}mvs_ms_ioffw_e;
/*************************************
 *单通道操作API族（不建议与双通道操作API混用！）
 ************************************/
/**
 * @brief 单通道数据取出定位准备（初始化取出handle）
 * 
 * @param phdl [in] 通道数据取出句柄（需要先设定好e_chn和pre_sec_ms）
 * @return 操作结果 
 * - success : MV_EC_SUCCESS
 * -  failed : MV_EC_INVALID_ARGS (空句柄参数)
 *             MV_EC_NOT_INIT     (通道未初始化)
 *             MV_EC_NOT_READY    (通道未准备好足够数据)
 * @note 成功有三种定位结果: \n
 *       1、定位到末尾帧（不一定是I帧）
 *       2、定位到目标提前时间量最接近的前一个I帧
 *       3、定位到目标提前时间量最接近的后一个I帧（差距是800ms以内）
 * @attention 注意，操作成功时；数据已经标记为引用
 */
mv_s32_t mvs_ms_data_prepare(mvs_ms_data_handle_t *phdl);
/**
 * @brief 单通道数据取出定位准备-优先根据I帧位置（初始化取出handle）
 * 
 * @param phdl       [in] 通道数据取出句柄（需要先设定好e_chn和pre_sec_ms）
 * @param s32_ioff   [in] 目标I帧相对偏移
 * @param s32_whence [in] 目标I帧位置查找方式[mvs_ms_ioffw_e]（当MS_ISEEK_INVALID时，等价于mvs_ms_data_prepare）
 * @return 操作结果 
 * - success : MV_EC_SUCCESS
 * -  failed : MV_EC_INVALID_ARGS (空句柄参数)
 *             MV_EC_NOT_INIT     (通道未初始化)
 *             MV_EC_NOT_READY    (通道未准备好足够数据)
 * @note 成功有三种定位结果: \n
 *       MS_ISEEK_INVALID时：
 *       1、定位到末尾帧（可能出现，不一定是I帧）
 *       2、定位到目标提前时间量最接近的前一个I帧
 *       3、定位到目标提前时间量最接近的后一个I帧（差距是800ms以内）
 *       MS_ISEEK_HDR_FORWARE / MS_ISEEK_END_BACKWARE 时：
 *       1、定位到目标I帧
 *       2、定位到最早的I帧/最新的I帧（超范围定位时出现）
 * @attention 注意，操作成功时；数据已经标记为引用
 */
mv_s32_t mvs_ms_data_prepare_by_ioff(mvs_ms_data_handle_t *phdl, mv_s32_t s32_ioff, mv_s32_t s32_whence);
/**
 * @brief 单通道数据取出
 * 
 * @param phdl [in] 通道数据取出句柄（需要先设定好e_chn和pre_sec_ms）
 * @param u8_wait_i_frame [in] 指定取出I帧（0=不需要，1=等待I帧）
 * @return 操作结果
 * - success : MV_EC_SUCCESS
 * -  failed : MV_EC_INVALID_ARGS (空句柄参数)
 *             MV_EC_NOT_READY    (通道未准备好下一个数据)
 *             MV_EC_OUT_OF_RANGE (超出通道下一帧索引范围，需要重新prepare)
 * @attention 注意，操作成功时；旧数据会自动解引用(drop)，无需重复调用mvs_ms_data_drop
 */
mv_s32_t mvs_ms_data_pop(mvs_ms_data_handle_t *phdl, mv_u8_t u8_wait_i_frame);

/************************
 *双通道操作API族（不建议与单通道操作API混用！）
 ***********************/
/**
 * @brief 双通道同步数据取出定位准备（初始化取出handle）
 * 
 * @param phdl  [in] 主通道句柄（需要先设定好e_chn和pre_sec_ms）
 * @param phdl2 [in] 从通道句柄（需要先设定好e_chn；pre_sec_ms会内部强制同步刷新）
 * @param s32_sync_timeout_ms [in] 同步定位超时时间（<=0表示不启用强制同步机制）
 * @param ps32_result [out] 操作结果码取出缓存
 * @return 准备好使用的数据句柄
 * - success : phandle (MV_EC_SUCCESS)
 * -  failed : NULL    (MV_EC_INVALID_ARGS) (主通道空句柄参数)
 *             NULL    (MV_EC_NOT_INIT)     (任一通道未初始化)
 *             NULL    (MV_EC_NOT_READY)    (任一通道未准备好足够数据)
 *             NULL    (MV_EC_TIMEOUT)      (超时未能取得同步)
 *             NULL    (MV_EC_FAILED)
 * @note 当从通道句柄为空(phdl2=NULL)时，自动等价单通道操作 \n
 *       主通道定位数据超前从通道定位数据，且小于300ms，则认为同步成功
 *       同步成功有三种定位结果: \n
 *       1、定位到末尾帧（不一定是I帧）
 *       2、定位到目标提前时间量最接近的前一个I帧
 *       3、定位到目标提前时间量最接近的后一个I帧（差距是800ms以内）
 * @attention 注意，操作成功时；两个通道的数据均已经标记为引用
 */
mvs_ms_data_handle_t *mvs_ms_data_chn_sync_prepare(mvs_ms_data_handle_t *phdl, mvs_ms_data_handle_t *phdl2, mv_s32_t s32_sync_timeout_ms, mv_s32ptr_t ps32_result);
/**
 * @brief 双通道同步数据取出定位准备（初始化取出handle）
 * 
 * @param phdl         [in] 主通道句柄（需要先设定好e_chn和pre_sec_ms）
 * @param phdl2        [in] 从通道句柄（需要先设定好e_chn；pre_sec_ms会内部强制同步刷新）
 * @param s32_sync_timeout_ms [in] 同步定位超时时间（<=0表示不启用强制同步机制）
 * @param s32_ioff     [in] 目标I帧相对偏移
 * @param s32_whence   [in] 目标I帧位置查找方式[mvs_ms_ioffw_e]（当MS_ISEEK_INVALID时，等价于mvs_ms_data_prepare）
 * @param ps32_result [out] 操作结果码取出缓存
 * @return 准备好使用的数据句柄
 * - success : phandle (MV_EC_SUCCESS)
 * -  failed : NULL    (MV_EC_INVALID_ARGS) (主通道空句柄参数)
 *             NULL    (MV_EC_NOT_INIT)     (任一通道未初始化)
 *             NULL    (MV_EC_NOT_READY)    (任一通道未准备好足够数据)
 *             NULL    (MV_EC_TIMEOUT)      (超时未能取得同步)
 *             NULL    (MV_EC_FAILED)
 * @note 当从通道句柄为空(phdl2=NULL)时，自动等价单通道操作 \n
 *       主通道定位数据超前从通道定位数据，且小于300ms，则认为同步成功
 *       同步成功有三种定位结果: \n
 *       MS_ISEEK_INVALID时：
 *       1、定位到末尾帧（可能出现，不一定是I帧）
 *       2、定位到目标提前时间量最接近的前一个I帧
 *       3、定位到目标提前时间量最接近的后一个I帧（差距是800ms以内）
 *       MS_ISEEK_HDR_FORWARE / MS_ISEEK_END_BACKWARE 时：
 *       1、定位到目标I帧
 *       2、定位到最早的I帧/最新的I帧（超范围定位时出现）
 * @attention 注意，操作成功时；两个通道的数据均已经标记为引用
 */
mvs_ms_data_handle_t *mvs_ms_data_chn_sync_prepare_ioff(mvs_ms_data_handle_t *phdl, mvs_ms_data_handle_t *phdl2, mv_s32_t s32_sync_timeout_ms, mv_s32_t s32_ioff, mv_s32_t s32_whence, mv_s32ptr_t ps32_result);
/**
 * @brief 双通道同步数据取出
 * 
 * @param phdl  [in] 主通道句柄（需要先设定好e_chn和pre_sec_ms）
 * @param phdl2 [in] 从通道句柄（需要先设定好e_chn；pre_sec_ms会内部强制同步刷新）
 * @param ps32_result [in] 操作结果码取出缓存
 * @return 准备好使用的数据句柄
 * - success : phandle (MV_EC_SUCCESS)
 * -  failed : NULL    (MV_EC_INVALID_ARGS) (主通道空句柄参数)
 *             NULL    (MV_EC_NOT_INIT)     (任一通道未初始化)
 *             NULL    (MV_EC_NOT_READY)    (任一通道未准备好足够数据)
 *             NULL    (MV_EC_OUT_OF_RANGE) (任一通道当前节点超出通道下一帧索引范围，需要重新prepare)
 * @note 当从通道句柄为空(phdl2=NULL)时，自动等价单通道操作 \n
 *       主通道定位数据超前从通道定位数据，且小于300ms，则认为同步成功
 * @attention 注意，操作成功时；对应句柄的旧数据会自动解引用(drop)，无需重复调用mvs_ms_data_drop \n
 *            不支持等待I帧操作，需要时，请使用prepare或连续pop出数据判断，来达到想要的效果
 */
mvs_ms_data_handle_t *mvs_ms_data_chn_sync_pop(mvs_ms_data_handle_t *phdl, mvs_ms_data_handle_t *phdl2, mv_s32ptr_t ps32_result);
/**
 * @brief 解除取出数据引用（包括prepare和pop成功拿到的数据）
 * 
 * @param phdl [in] 通道数据取出句柄（需要先设定好e_chn和pre_sec_ms）
 * @return 操作结果 
 * - success : MV_EC_SUCCESS
 * -  failed : MV_EC_INVALID_ARGS (空句柄参数/未有数据引用)
 *             MV_EC_NOT_READY    (通道未准备好下一个数据)
 * @note 句柄一旦drop后，下次使用前必须prepare，才能正常pop操作
 * @attention 注意，句柄或数据长时间不使用时，必须调用drop解引用，避免对数据的长期无效占用 ！
 */
mv_s32_t mvs_ms_data_drop(mvs_ms_data_handle_t *phdl);

MVCXX_EXTERN_END

#endif
